﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Cesave.Domain.Core;
using Cesave.Domain.Core.Entities;
using Cesave.Domain.Core.Specification;
using Cesave.Infrastructure.CrossCutting.IoC;
using Cesave.Infrastructure.CrossCutting.Logging;
using Cesave.Infrastructure.Data.Core;
using Cesave.Infrastructure.Data.PermisosModule.Context;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Cesave.Infrastructure.Data.PermisosModule.Tests
{
    /// <summary>
    /// This is a base class for testing repositories. This base class
    /// implement all method defined in IRepository for any type. Inherit
    /// of this class to add test implementation in a concrete repository.
    /// For view all tests correctly in a TestView windows please add column Full Class Name
    /// </summary>
    /// <typeparam name="TEntity">Inner type of respository</typeparam>
    [TestClass]
    [DeploymentItem("Cesave.Infrastructure.Data.PermisosModule.Mock.dll")]
    public abstract class GenericRepositoryTestsBase<TEntity>
        where TEntity : class,IObjectWithChangeTracker, new()
    {

        #region Virtual and abstract elements for inheritance unit tests

        /// <summary>
        /// Specification of filter expression for a particular type
        /// </summary>
        public abstract Expression<Func<TEntity, bool>> FilterExpression
        {
            get;
        }
        /// <summary>
        /// Specification of order by expression for a particular type
        /// </summary>
        public abstract Expression<Func<TEntity, int>> OrderByExpression
        {
            get;
        }

        /// <summary>
        /// Get true if  a particular type is a parent of a hierarchy. If is true
        /// this base class add test for derived types in hierarchy
        /// </summary>
        public virtual bool HashNestedTypes
        {
            get
            {
                return false;
            }
        }

        #endregion

        #region Test Helper for testing purposes
        /// <summary>
        /// Get Active context for testing
        /// </summary>
        /// <param name="initializeContainer">True if context is initialized</param>
        /// <returns>Active context for testing</returns>
        public IPermisosModuleContext GetContext(bool initializeContainer = true)
        {
            // Get context specified in unity configuration
            // Set active context for 
            // testing with fake or real context in application configuration file 
            // "defaultIoCContainer" setting

            return IoCFactory.Resolve<IPermisosModuleContext>();
        }
        public ITraceManager GetTraceManager()
        {
            //Get configured trace manager

            return IoCFactory.Resolve<ITraceManager>();
        }

        #endregion

        #region Test Methods


        [TestMethod]
        public virtual void GenericRepository_InvokeConstructor_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            IContext actual = repository.StoreContext;

            //Assret
            Assert.AreEqual(context, actual);
        }
        [TestMethod]
        public virtual void GenericRepository_AddValidItem_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            TEntity item = new TEntity();
            repository.Add(item);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public virtual void GenericRepository_AddNullItemThrowArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();
            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            repository.Add(null);
        }
        [TestMethod]
        public virtual void GenericRepository_DeleteValidItemTest()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            TEntity item = new TEntity();
            repository.Remove(item);

            //Assert
            //TODO:Add check condition here
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public virtual void GenericRepository_DeleteNullItemThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            repository.Remove(null);
        }
        [TestMethod]
        public virtual void GenericRepository_ApplyChangesValidItem_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            TEntity item = new TEntity();
            repository.Modify(item);


            //Assert
            //TODO:Add check condition here
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public virtual void GenericRepositoryApplyChangesWithNullItemThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            repository.Modify((TEntity)null);
        }
        [TestMethod]
        public virtual void GenericRepository_ApplyChangesInCollection_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepositoryEx<TEntity> repository = new GenericRepositoryEx<TEntity>(context, traceManager);
            List<TEntity> items = new List<TEntity>();
            items.Add(new TEntity());
            repository.Modify(items);

            //Assert
            //TODO:Add check condition here
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public virtual void GenericRepository_ApplyChangesWithNullCollectionThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepositoryEx<TEntity> repository = new GenericRepositoryEx<TEntity>(context, traceManager);
            repository.Modify((List<TEntity>)null);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public virtual void GenericRepository_ConstructorWithNullContainerThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = null;
            ITraceManager traceManager = this.GetTraceManager();

            //Act
            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
        }
        [TestMethod]
        public virtual void GetAllTest()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            IEnumerable<TEntity> entities = repository.GetAll();

            //If Entity is parent in hierarchy invoke GetAll<K> where K:TEntity
            //for all K types in assembly
            if (HashNestedTypes)
            {

                foreach (Type type in typeof(TEntity).Assembly.GetExportedTypes())
                {
                    if (typeof(TEntity) != type && typeof(TEntity).IsAssignableFrom(type))
                    {
                        MethodInfo genericGetAll = (from m
                                                        in repository.GetType().GetMethods()
                                                    where m.Name == "GetAll" && m.IsGenericMethod
                                                    select m).First();

                        var result = genericGetAll.MakeGenericMethod(new Type[] { type })
                                                  .Invoke(repository, null);

                        //Assert
                        Assert.IsNotNull(result);
                    }
                }
            }
        }
        [TestMethod]
        public virtual void GenericRepository_GetFilteredElements_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements(FilterExpression);

            //Assert
            Assert.IsNotNull(entities);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetFilteredElementsWithNullFilterThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements(null);
        }
        [TestMethod]
        public void GenericRepository_GetFilteredElementsWithOrderAscending_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements<int>(FilterExpression, OrderByExpression, true);

            //Assert
            Assert.IsNotNull(entities);
        }
        [TestMethod]
        public void GenericRepository_GetFilteredElementsWithOrderDescending_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements<int>(FilterExpression, OrderByExpression, false);

            //Assert
            Assert.IsNotNull(entities);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetFilteredElementsWithOrderNullThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements<int>(FilterExpression, null, false);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetFilteredElementsWithFilterNullAndOrderNotNullThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements<int>(null, OrderByExpression, false);
        }
        [TestMethod]
        public void GenericRepository_GetFilteredElementsWithPaggingAscending_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 0;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements(FilterExpression, pageIndex, pageCount, OrderByExpression, true);

            //Assert
            Assert.IsNotNull(entities);

        }
        [TestMethod]
        public void GenericRepository_GetFilteredElementsWithPaggingDescending_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 0;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements(FilterExpression, pageIndex, pageCount, OrderByExpression, false);

            //Assert
            Assert.IsNotNull(entities);

        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void GenericRepository_GetFilteredElementsWithPaggingInvalidPageIndexThrowNewArgumentException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = -1;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements(FilterExpression, pageIndex, pageCount, OrderByExpression, false);

        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void GenericRepository_GetFilteredElementsWithPaggingInvalidPageCountThrowNewArgumentException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 1;
            int pageCount = 0;

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements(FilterExpression, pageIndex, pageCount, OrderByExpression, false);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetFilteredElementsWithPaggingNullOrderExpressionThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 1;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements<int>(FilterExpression, pageIndex, pageCount, null, false);

        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetFilteredElementsWithPaggingNullFilterExpressionThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 1;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetFilteredElements<int>(null, pageIndex, pageCount, null, false);

        }
        [TestMethod]
        public void GenericrRepository_GetPagedElementsAscending_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 0;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetPagedElements<int>(pageIndex, pageCount, OrderByExpression, true);

            //Assert
            Assert.IsNotNull(entities);

        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void GenericRepository_GetPagedElementsInvalidPageIndexThrowNewArgumentException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = -1;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetPagedElements<int>(pageIndex, pageCount, OrderByExpression, false);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void GenericRepository_GetPagedElementsInvalidPageCount_ThrowNewArgumentException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 0;
            int pageCount = 0;

            //Act
            IEnumerable<TEntity> entities = repository.GetPagedElements<int>(pageIndex, pageCount, OrderByExpression, false);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetPagedElementsInvalidOrderByExpressionThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 0;
            int pageCount = 1;

            //Act
            IEnumerable<TEntity> entities = repository.GetPagedElements<int>(pageIndex, pageCount, null, false);
        }
        [TestMethod]
        public void GenericRepository_GetPagedElementsDescending_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 0;
            int pageCount = 1;
            bool ascending = false;

            //Act
            IEnumerable<TEntity> entities = repository.GetPagedElements<int>(pageIndex, pageCount, OrderByExpression, ascending);

            //Assert
            Assert.IsNotNull(entities);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetPagedElementsWithNullSpecThrowNewArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            int pageIndex = 0;
            int pageCount = 10;

            //Act
            IEnumerable<TEntity> entities = repository.GetPagedElements<int>(pageIndex, pageCount, OrderByExpression, null, false);
        }
        [TestMethod]
        public void GenericRepository_GetPagedElementsWithSpec_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            ISpecification<TEntity> spec = new TrueSpecification<TEntity>();

            int pageIndex = 0;
            int pageCount = 10;

            //Act
            IEnumerable<TEntity> entities = repository.GetPagedElements<int>(pageIndex, pageCount, OrderByExpression, spec, false);
        }
        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void GenericRepository_GetBySpecWithNullSpecThrowArgumentNullException_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);

            //Act
            repository.GetBySpec((ISpecification<TEntity>)null);
        }
        [TestMethod]
        public void GenericRepository_GetBySpecDirectSpec_Invoke_Test()
        {
            //Arrange
            IQueryableContext context = GetContext();
            ITraceManager traceManager = this.GetTraceManager();

            GenericRepository<TEntity> repository = new GenericRepository<TEntity>(context, traceManager);
            ISpecification<TEntity> specification = new DirectSpecification<TEntity>(this.FilterExpression);

            //Act
            IEnumerable<TEntity> result = repository.GetBySpec(specification);

            //Assert
            Assert.IsNotNull(result);
        }
        #endregion
    }
}
